package com.voiceqsologger.service;

import ai.djl.util.JsonUtils;
import cn.smartjavaai.common.entity.Language;
import cn.smartjavaai.common.entity.R;
import cn.smartjavaai.speech.asr.config.AsrModelConfig;
import cn.smartjavaai.speech.asr.entity.AsrResult;
import cn.smartjavaai.speech.asr.entity.WhisperParams;
import cn.smartjavaai.speech.asr.enums.AsrModelEnum;
import cn.smartjavaai.speech.asr.factory.SpeechRecognizerFactory;
import cn.smartjavaai.speech.asr.model.SpeechRecognizer;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.voiceqsologger.config.AsrWhisperProperties;
import com.voiceqsologger.config.AsrModeProperties;
import com.voiceqsologger.entity.AsrTask;
import com.voiceqsologger.entity.QsoLog;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;

/**
 * ASR 服务：封装 Whisper 识别流程
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AsrService {

    private final AsrWhisperProperties properties;
    private final AsrModeProperties modeProperties;
    private final ApiAsrClient apiAsrClient;
    private final LlmQsoExtractorClient llmExtractor;
    private final QsoLogService qsoLogService;

    /**
     * 新建识别任务并启动识别
     * @param file 上传的音频文件
     * @param uploadDir 存储目录
     * @return 任务ID
     */
    public String createAndStart(MultipartFile file, String uploadDir) {
        String taskId = IdWorker.getIdStr();
        LocalDateTime now = LocalDateTime.now();
        Path dir = Paths.get(uploadDir);
        try {
            Files.createDirectories(dir);
            String original = file.getOriginalFilename();
            Path saved = dir.resolve(taskId + "_" + (original == null ? "audio" : original));
            file.transferTo(saved.toFile());

            AsrTask task = new AsrTask();
            task.setId(taskId);
            task.setFilename(original);
            task.setFilePath(saved.toString());
            task.setLanguage(properties.getLanguage());
            task.setStatus("PENDING");
            task.setCreatedAt(now);
            Db.save(task);

            // 根据全局模式异步执行识别
            new Thread(() -> {
                String mode = modeProperties.getMode();
                if ("api".equalsIgnoreCase(mode)) runCloud(taskId, saved.toString());
                else runWhisper(taskId, saved.toString());
            }).start();
            return taskId;
        } catch (Exception e) {
            throw new RuntimeException("保存文件失败", e);
        }
    }

    /**
     * 新建识别任务（指定模式）并启动识别
     * @param file 上传的音频文件
     * @param uploadDir 存储目录
     * @param mode 模式：local 或 api
     * @return 任务ID
     */
    public String createAndStart(MultipartFile file, String uploadDir, String mode) {
        String taskId = IdWorker.getIdStr();
        LocalDateTime now = LocalDateTime.now();
        Path dir = Paths.get(uploadDir);
        try {
            Files.createDirectories(dir);
            String original = file.getOriginalFilename();
            Path saved = dir.resolve(taskId + "_" + (original == null ? "audio" : original));
            file.transferTo(saved.toFile());

            AsrTask task = new AsrTask();
            task.setId(taskId);
            task.setFilename(original);
            task.setFilePath(saved.toString());
            task.setLanguage(properties.getLanguage());
            task.setStatus("PENDING");
            task.setCreatedAt(now);
            Db.save(task);

            // 若外部传了 mode 参数则优先使用，否则用全局配置
            new Thread(() -> {
                String m = (mode == null || mode.isEmpty()) ? modeProperties.getMode() : mode;
                if ("api".equalsIgnoreCase(m)) runCloud(taskId, saved.toString());
                else runWhisper(taskId, saved.toString());
            }).start();
            return taskId;
        } catch (Exception e) {
            throw new RuntimeException("保存文件失败", e);
        }
    }

    /**
     * 执行 Whisper 识别
     */
    private void runWhisper(String taskId, String filePath) {
        try {
            // 标记处理中
            Db.updateById(updateStatus(taskId, "PROCESSING", null));

            // 配置 Whisper 识别器
            AsrModelConfig config = new AsrModelConfig();
            config.setModelEnum(AsrModelEnum.WHISPER);
            config.setModelPath(properties.getModelPath());
            SpeechRecognizer recognizer = SpeechRecognizerFactory.getInstance().getModel(config);

            WhisperParams params = new WhisperParams();
            params.setLanguage(Language.valueOf(properties.getLanguage()));
            // 提示模型使用简体中文
            io.github.givimad.whisperjni.WhisperFullParams fullParams = new io.github.givimad.whisperjni.WhisperFullParams(io.github.givimad.whisperjni.WhisperSamplingStrategy.BEAN_SEARCH);
            fullParams.initialPrompt = "简体中文";
            params.setParams(fullParams);

            long start = System.currentTimeMillis();
            long asrStart = System.currentTimeMillis();
            R<AsrResult> result = recognizer.recognize(filePath, params);
            if (result.isSuccess()) {
                AsrResult data = result.getData();
                log.info("识别成功: {}", JsonUtils.toJson(data));
                AsrTask ok = new AsrTask();
                ok.setId(taskId);
                ok.setStatus("SUCCESS");
                ok.setText(data.getText());
                ok.setFinishedAt(LocalDateTime.now());
                Db.updateById(ok);

                // 触发大模型解析并保存通联日志
                try {
                    long llmStart = System.currentTimeMillis();
                    QsoLog parsed = llmExtractor.extract(data.getText());
                    if (parsed != null) {
                        parsed.setRawText(data.getText());
                        qsoLogService.save(parsed);
                    }
                    log.info("ASR cost={}ms, LLM cost={}ms", (System.currentTimeMillis()-asrStart), (System.currentTimeMillis()-llmStart));
                } catch (Exception ignore) {}
            } else {
                AsrTask fail = new AsrTask();
                fail.setId(taskId);
                fail.setStatus("FAILED");
                fail.setErrorMsg(result.getMessage());
                fail.setFinishedAt(LocalDateTime.now());
                Db.updateById(fail);
            }
            log.info("cost={}ms", System.currentTimeMillis() - start);
        } catch (Exception e) {
            AsrTask fail = new AsrTask();
            fail.setId(taskId);
            fail.setStatus("FAILED");
            fail.setErrorMsg(e.getMessage());
            fail.setFinishedAt(LocalDateTime.now());
            Db.updateById(fail);
            log.error("识别异常", e);
        }
    }

    /**
     * 执行 云 API 识别
     */
    private void runCloud(String taskId, String filePath) {
        try {
            Db.updateById(updateStatus(taskId, "PROCESSING", null));
            long start = System.currentTimeMillis();
            long asrStart = System.currentTimeMillis();
            String text = apiAsrClient.transcribe(Paths.get(filePath));
            AsrTask ok = new AsrTask();
            ok.setId(taskId);
            ok.setStatus("SUCCESS");
            ok.setText(text);
            ok.setFinishedAt(LocalDateTime.now());
            Db.updateById(ok);
            log.info("云 API 识别成功, cost={}ms, text={}", System.currentTimeMillis() - start, text);

            // 触发大模型解析并保存通联日志
            try {
                long llmStart = System.currentTimeMillis();
                QsoLog parsed = llmExtractor.extract(text);
                if (parsed != null) {
                    parsed.setRawText(text);
                    qsoLogService.save(parsed);
                }
                log.info("ASR cost={}ms, LLM cost={}ms", (System.currentTimeMillis()-asrStart), (System.currentTimeMillis()-llmStart));
            } catch (Exception ignore) {}
        } catch (Exception e) {
            AsrTask fail = new AsrTask();
            fail.setId(taskId);
            fail.setStatus("FAILED");
            fail.setErrorMsg(e.getMessage());
            fail.setFinishedAt(LocalDateTime.now());
            Db.updateById(fail);
            log.error("云 API 识别异常", e);
        }
    }

    private AsrTask updateStatus(String id, String status, String msg) {
        AsrTask t = new AsrTask();
        t.setId(id);
        t.setStatus(status);
        t.setErrorMsg(msg);
        return t;
    }
}


